home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / futilsrc.zoo / fileutil / src / mv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-19  |  9.1 KB  |  438 lines

  1. /* mv -- move or rename files
  2.    Copyright (C) 1986, 1989-1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Options:
  19.    -f, +force        Assume a 'y' answer to all questions it would
  20.             normally ask, and not ask the questions.
  21.  
  22.    -i, +interactive    Require confirmation from the user before
  23.             performing any move that would destroy an
  24.             existing file. 
  25.  
  26.    -u, +update        Do not move a nondirectory that has an
  27.             existing destination with the same or newer
  28.             modification time.  
  29.  
  30.    -v, +verbose        List the name of each file as it is moved, and
  31.             the name it is moved to. 
  32.  
  33.    -b, +backup
  34.    -S, +suffix
  35.    -V, +version-control
  36.             Backup file creation.
  37.  
  38.    Written by Mike Parker and David MacKenzie */
  39.  
  40. #ifdef _AIX
  41.  #pragma alloca
  42. #endif
  43. #include <stdio.h>
  44. #include <getopt.h>
  45. #include <sys/types.h>
  46. #include "system.h"
  47. #include "backupfile.h"
  48.  
  49. enum backup_type get_version ();
  50. int copy ();
  51. int do_move ();
  52. int eaccess_stat ();
  53. int isdir ();
  54. int movefile ();
  55. int yesno ();
  56. void error ();
  57. void strip_trailing_slashes ();
  58. void usage ();
  59.  
  60. /* The name this program was run with. */
  61. char *program_name;
  62.  
  63. /* If nonzero, query the user before overwriting files. */
  64. int interactive;
  65.  
  66. /* If nonzero, do not query the user before overwriting unwritable
  67.    files. */
  68. int override_mode;
  69.  
  70. /* If nonzero, do not move a nondirectory that has an existing destination
  71.    with the same or newer modification time. */
  72. int update = 0;
  73.  
  74. /* If nonzero, list each file as it is moved. */
  75. int verbose;
  76.  
  77. /* If nonzero, stdin is a tty. */
  78. int stdin_tty;
  79.  
  80. struct option long_options[] =
  81. {
  82.   {"backup", 0, NULL, 'b'},
  83.   {"force", 0, NULL, 'f'},
  84.   {"interactive", 0, NULL, 'i'},
  85.   {"suffix", 1, NULL, 'S'},
  86.   {"update", 0, &update, 1},
  87.   {"verbose", 0, &verbose, 1},
  88.   {"version-control", 1, NULL, 'V'},
  89.   {NULL, 0, NULL, 0}
  90. };
  91.  
  92. void
  93. main (argc, argv)
  94.      int argc;
  95.      char **argv;
  96. {
  97.   int c;
  98.   int errors;
  99.   int make_backups = 0;
  100.   char *version;
  101.  
  102.   version = getenv ("SIMPLE_BACKUP_SUFFIX");
  103.   if (version)
  104.     simple_backup_suffix = version;
  105.   version = getenv ("VERSION_CONTROL");
  106.   program_name = argv[0];
  107.   interactive = override_mode = verbose = update = 0;
  108.   errors = 0;
  109.  
  110.   while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, (int *) 0))
  111.      != EOF)
  112.     {
  113.       switch (c)
  114.     {
  115.     case 0:
  116.       break;
  117.     case 'b':
  118.       make_backups = 1;
  119.       break;
  120.     case 'f':
  121.       interactive = 0;
  122.       override_mode = 1;
  123.       break;
  124.     case 'i':
  125.       interactive = 1;
  126.       override_mode = 0;
  127.       break;
  128.     case 'u':
  129.       update = 1;
  130.       break;
  131.     case 'v':
  132.       verbose = 1;
  133.       break;
  134.     case 'S':
  135.       simple_backup_suffix = optarg;
  136.       break;
  137.     case 'V':
  138.       version = optarg;
  139.       break;
  140.     default:
  141.       usage ();
  142.     }
  143.     }
  144.   if (argc < optind + 2)
  145.     usage ();
  146.  
  147.   if (make_backups)
  148.     backup_type = get_version (version);
  149.  
  150.   stdin_tty = isatty (0);
  151.  
  152.   strip_trailing_slashes (argv[argc - 1]);
  153.  
  154.   if (argc > optind + 2 && !isdir (argv[argc - 1]))
  155.     error (1, 0, "when moving multiple files, last argument must be a directory");
  156.  
  157.   /* Move each arg but the last onto the last. */
  158.   for (; optind < argc - 1; ++optind)
  159.     errors |= movefile (argv[optind], argv[argc - 1]);
  160.  
  161.   exit (errors);
  162. }
  163.  
  164. /* Move file FROM onto TO.  Handles the case when TO is a directory.
  165.    Return 0 if successful, 1 if an error occurred.  */
  166.  
  167. int
  168. movefile (from, to)
  169.      char *from;
  170.      char *to;
  171. {
  172.   strip_trailing_slashes (from);
  173.   if (isdir (to))
  174.     {
  175.       /* Target is a directory; build full target filename. */
  176.       char *cp;
  177.       char *newto;
  178.  
  179.       cp = rindex (from, '/');
  180.       if (cp)
  181.     cp++;
  182.       else
  183.     cp = from;
  184.  
  185.       newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
  186.       sprintf (newto, "%s/%s", to, cp);
  187.       return do_move (from, newto);
  188.     }
  189.   else
  190.     return do_move (from, to);
  191. }
  192.  
  193. struct stat to_stats, from_stats;
  194.  
  195. /* Move FROM onto TO.  Handles cross-filesystem moves.
  196.    If TO is a directory, FROM must be also.
  197.    Return 0 if successful, 1 if an error occurred.  */
  198.  
  199. int
  200. do_move (from, to)
  201.      char *from;
  202.      char *to;
  203. {
  204.   char *to_backup = NULL;
  205.  
  206.   if (lstat (from, &from_stats) != 0)
  207.     {
  208.       error (0, errno, "%s", from);
  209.       return 1;
  210.     }
  211.  
  212.   if (lstat (to, &to_stats) == 0)
  213.     {
  214.       if (from_stats.st_dev == to_stats.st_dev
  215.       && from_stats.st_ino == to_stats.st_ino)
  216.     {
  217.       error (0, 0, "`%s' and `%s' are the same file", from, to);
  218.       return 1;
  219.     }
  220.  
  221.       if (S_ISDIR (to_stats.st_mode))
  222.     {
  223.       error (0, 0, "%s: cannot overwrite directory", to);
  224.       return 1;
  225.     }
  226.  
  227.       if (!S_ISDIR (from_stats.st_mode) && update
  228.       && from_stats.st_mtime <= to_stats.st_mtime)
  229.     return 0;
  230.  
  231.       if (!override_mode && (interactive || stdin_tty)
  232.       && eaccess_stat (&to_stats, W_OK))
  233.     {
  234.       fprintf (stderr, "%s: replace `%s', overriding mode %04o? ",
  235.            program_name, to, to_stats.st_mode & 07777);
  236.       if (!yesno ())
  237.         return 0;
  238.     }
  239.       else if (interactive)
  240.     {
  241.       fprintf (stderr, "%s: replace `%s'? ", program_name, to);
  242.       if (!yesno ())
  243.         return 0;
  244.     }
  245.  
  246.       if (backup_type != none)
  247.     {
  248.       char *tmp_backup = find_backup_file_name (to);
  249.       if (tmp_backup == NULL)
  250.         error (1, 0, "virtual memory exhausted");
  251.       to_backup = alloca (strlen (tmp_backup) + 1);
  252.       strcpy (to_backup, tmp_backup);
  253.       free (tmp_backup);
  254.       if (rename (to, to_backup))
  255.         {
  256.           if (errno != ENOENT)
  257.         {
  258.           error (0, errno, "cannot backup `%s'", to);
  259.           return 1;
  260.         }
  261.           else
  262.         to_backup = NULL;
  263.         }
  264.     }
  265.     }
  266.   else if (errno != ENOENT)
  267.     {
  268.       error (0, errno, "%s", to);
  269.       return 1;
  270.     }
  271.  
  272.   if (verbose)
  273.     printf ("%s -> %s\n", from, to);
  274.  
  275.   if (rename (from, to) == 0)
  276.     {
  277.       return 0;
  278.     }
  279.  
  280.   if (errno != EXDEV)
  281.     {
  282.       error (0, errno, "cannot move `%s' to `%s'", from, to);
  283.       goto un_backup;
  284.     }
  285.  
  286.   /* rename failed on cross-filesystem link.  Copy the file instead. */
  287.  
  288.   if (copy (from, to))
  289.     goto un_backup;
  290.   
  291.   if (unlink (from))
  292.     {
  293.       error (0, errno, "cannot remove `%s'", from);
  294.       return 1;
  295.     }
  296.  
  297.   return 0;
  298.  
  299.  un_backup:
  300.   if (to_backup)
  301.     {
  302.       if (rename (to_backup, to))
  303.     error (0, errno, "cannot un-backup `%s'", to);
  304.     }
  305.   return 1;
  306. }
  307.  
  308. /* Copy file FROM onto file TO.
  309.    Return 1 if an error occurred, 0 if successful. */
  310.  
  311. int
  312. copy (from, to)
  313.      char *from, *to;
  314. {
  315.   int ifd;
  316.   int ofd;
  317.   char buf[1024 * 8];
  318.   int len;            /* Number of bytes read into `buf'. */
  319.   
  320.   if (!S_ISREG (from_stats.st_mode))
  321.     {
  322.       error (0, 0, "cannot move `%s' across filesystems: Not a regular file",
  323.          from);
  324.       return 1;
  325.     }
  326.   
  327.   if (unlink (to) && errno != ENOENT)
  328.     {
  329.       error (0, errno, "cannot remove `%s'", to);
  330.       return 1;
  331.     }
  332.  
  333.   ifd = open (from, O_RDONLY, 0);
  334.   if (ifd < 0)
  335.     {
  336.       error (0, errno, "%s", from);
  337.       return 1;
  338.     }
  339.   ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  340.   if (ofd < 0)
  341.     {
  342.       error (0, errno, "%s", to);
  343.       close (ifd);
  344.       return 1;
  345.     }
  346. #ifndef FCHMOD_MISSING
  347.   if (fchmod (ofd, from_stats.st_mode & 0777))
  348.     {
  349.       error (0, errno, "%s", to);
  350.       close (ifd);
  351.       close (ofd);
  352.       unlink (to);
  353.       return 1;
  354.     }
  355. #endif
  356.   
  357.   while ((len = read (ifd, buf, sizeof (buf))) > 0)
  358.     {
  359.       int wrote = 0;
  360.       char *bp = buf;
  361.       
  362.       do
  363.     {
  364.       wrote = write (ofd, bp, len);
  365.       if (wrote < 0)
  366.         {
  367.           error (0, errno, "%s", to);
  368.           close (ifd);
  369.           close (ofd);
  370.           unlink (to);
  371.           return 1;
  372.         }
  373.       bp += wrote;
  374.       len -= wrote;
  375.     } while (len > 0);
  376.     }
  377.   if (len < 0)
  378.     {
  379.       error (0, errno, "%s", from);
  380.       close (ifd);
  381.       close (ofd);
  382.       unlink (to);
  383.       return 1;
  384.     }
  385.  
  386.   if (close (ifd) < 0)
  387.     {
  388.       error (0, errno, "%s", from);
  389.       close (ofd);
  390.       return 1;
  391.     }
  392.   if (close (ofd) < 0)
  393.     {
  394.       error (0, errno, "%s", to);
  395.       return 1;
  396.     }
  397.   
  398. #ifdef FCHMOD_MISSING
  399.   if (chmod (to, from_stats.st_mode & 0777))
  400.     {
  401.       error (0, errno, "%s", to);
  402.       return 1;
  403.     }
  404. #endif
  405.  
  406.   /* Try to copy the old file's modtime and access time.  */
  407.   {
  408.     struct utimbuf tv;
  409.  
  410.     tv.actime = from_stats.st_atime;
  411.     tv.modtime = from_stats.st_mtime;
  412.     if (utime (to, &tv))
  413.       {
  414.     error (0, errno, "%s", to);
  415.     return 1;
  416.       }
  417.   }
  418.  
  419.   if (chown (to, from_stats.st_uid, from_stats.st_gid) < 0 && errno != EPERM)
  420.     error (0, errno, "%s", to);
  421.  
  422.   return 0;
  423. }
  424.  
  425. void
  426. usage ()
  427. {
  428.   fprintf (stderr, "\
  429. Usage: %s [options] source dest\n\
  430.        %s [options] source... directory\n\
  431. Options:\n\
  432.        [-bfiuv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  433.        [+backup] [+force] [+interactive] [+update] [+verbose]\n\
  434.        [+suffix=backup-suffix] [+version-control={numbered,existing,simple}]\n",
  435.        program_name, program_name);
  436.   exit (1);
  437. }
  438.